package collection

import (
	"gitee.com/mootok/mootok/src/constant/strings"
	"gitee.com/mootok/mootok/src/models"
	"gitee.com/mootok/mootok/src/services/video"
	"gitee.com/mootok/mootok/src/storage/database"
	"gitee.com/mootok/mootok/src/utils/logging"
	"gitee.com/mootok/mootok/src/web/api"
	"gitee.com/mootok/mootok/src/web/common/response"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"net/http"
)

type ApiCollectionImpl struct {
}

var videoService video.Service

func (*ApiCollectionImpl) CreateCollection(c *gin.Context) {
	createCollectionReq := api.CreateCollectionReq{}
	err := c.ShouldBindJSON(&createCollectionReq)
	//400
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}

	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Collections.Post").WithFields(logrus.Fields{
		"user_id": userId,
		"name":    createCollectionReq.Name,
	})

	if err := database.Client.Create(&models.Collection{
		UserId: userId,
		Name:   createCollectionReq.Name,
	}).Error; err != nil {
		logger.Errorf("collection creation to db failed:%v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
		return
	}
	response.Success(c, strings.SuccessCode)
	logger.Errorf("collection creation success.")
}

func (*ApiCollectionImpl) RemoveCollection(c *gin.Context) {
	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	//400
	removeCollectionBatchReq := api.RemoveCollectionBatchReq{}
	err := c.ShouldBindJSON(&removeCollectionBatchReq)
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}

	collectionIds := removeCollectionBatchReq.CollectionIds

	logger := logging.LogService("Collections.Delete").WithFields(logrus.Fields{
		"user_id":        userId,
		"collection_ids": collectionIds,
	})

	//开启事务
	tx := database.Client.WithContext(c).Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
			logger.Errorf("rollback:%v", err)
			return
		}
		tx.Commit()
	}()

	//delete from collections
	if err = tx.Where("id in (?)", collectionIds).Where("user_id = ?", userId).
		Delete(&models.Collection{}).
		Error; err != nil {
		logger.Errorf("collection remove from db failed:%v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseDeleteErrorCode, strings.InternalServiceError)
		return
	}

	//delete from user_video_collection_relations
	if err = tx.Where("collection_id in (?)", collectionIds).Where("user_id = ?", userId).
		Delete(&models.UserVideoCollectionRelation{}).Error; err != nil {
		logger.Errorf(" user_video_collection_relations remove from db failed:%v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseDeleteErrorCode, strings.InternalServiceError)
		return
	}

	response.Success(c, strings.SuccessCode)
	logger.Errorf("removeCollection success.")
}

func (*ApiCollectionImpl) PageCollection(c *gin.Context, params api.PageCollectionParams) {
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)
	var collections []models.Collection
	pageNo := params.PageNo
	pageSize := params.PageSize
	offset := (pageNo - 1) * pageSize

	logger := logging.LogService("Collections.Get").WithFields(logrus.Fields{
		"user_id":   userId,
		"page_size": params.PageNo,
		"page_no":   pageNo,
	})

	//404
	result := database.Client.Where("user_id = ?", userId).Order("id").
		Limit(pageSize).Offset(offset).Find(&collections)

	if err := result.Error; err != nil {
		logger.Errorf(" collections select from db failed:%v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	var count int64
	result.Count(&count)
	pageCount := count / int64(pageSize)
	if pageCount%int64(pageSize) != 0 {
		pageCount++
	}

	pageCollectionRespItemList := make([]api.PageCollectionRespItem, len(collections))

	for i := 0; i < len(pageCollectionRespItemList); i++ {
		var coverList []string
		pageCollectionRespItemList[i].Id = &collections[i].ID
		pageCollectionRespItemList[i].Name = &collections[i].Name
		err := database.Client.Table("user_video_collection_relations").
			Joins("join videos on user_video_collection_relations.video_id = videos.id").
			Where("collection_id = ? ", collections[i].ID).
			Limit(6).
			Pluck("file_name", &coverList).Error

		for j := 0; j < len(coverList); j++ {
			coverList[j] = videoService.GetVideoCover(coverList[j])
		}

		if err != nil {
			logger.Errorf("videos.file_name select from db failed:%v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}
		pageCollectionRespItemList[i].CoverList = &coverList

		var count int64
		err = database.Client.Table("user_video_collection_relations").
			Joins("join videos on user_video_collection_relations.video_id = videos.id").
			Where("collection_id = ? ", collections[i].ID).Count(&count).Error

		if err != nil {
			logger.Errorf("count videos  from db:%v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}

		pageCollectionRespItemList[i].Count = &count
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, &api.PageCollectionResp{
		PageNo:    &pageNo,
		PageSize:  &pageSize,
		PageCount: &pageCount,
		Count:     &count,
		Items:     &pageCollectionRespItemList,
	})
	logger.Errorf("pageCollection success.")
}

func (*ApiCollectionImpl) UpdateCollectionById(c *gin.Context, id int) {

	//400
	updateCollectionByIdReq := api.UpdateCollectionByIdReq{}
	err := c.ShouldBindJSON(&updateCollectionByIdReq)

	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}
	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Collections.Update").WithFields(logrus.Fields{
		"user_id": userId,
		"name":    updateCollectionByIdReq.Name,
		"id":      id,
	})

	//update
	if database.Client.Model(&models.Collection{}).Where("id = ?", id).
		Where("user_id = ?", userId).Updates(models.Collection{
		Name: updateCollectionByIdReq.Name,
	}).Error != nil {
		logger.Errorf("collection update to db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseUpdateErrorCode, strings.InternalServiceError)
		return
	}

	response.Success(c, strings.SuccessCode)
	logger.Errorf("collection update success")
}

func (*ApiCollectionImpl) SaveVideoToCollection(c *gin.Context) {
	//400
	saveVideoToCollectionReq := api.SaveVideoToCollectionReq{}
	err := c.ShouldBindJSON(&saveVideoToCollectionReq)
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}

	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)
	videoId := saveVideoToCollectionReq.VideoId

	logger := logging.LogService("Collections.Videos.SaveVideoToCollection").WithFields(logrus.Fields{
		"user_id":        userId,
		"collection_ids": saveVideoToCollectionReq.CollectionIds,
		"video_id":       saveVideoToCollectionReq.VideoId,
	})

	//404 视频不存在
	if err := database.Client.Where("id = ?", videoId).First(&models.Video{}).Error; err != nil {
		logger.Errorf("video select from db failed %v", err)
		response.FailWithMsg(c, http.StatusNotFound, strings.NotFoundErrorCode,
			strings.NotFoundError)
		return
	}

	//收藏夹id
	collectionIds := saveVideoToCollectionReq.CollectionIds
	userVideoCollectionRelationList := make([]models.UserVideoCollectionRelation, len(collectionIds))

	tx := database.Client.WithContext(c).Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
			logger.Errorf("rollback:%v", err)
			return
		}
		tx.Commit()
	}()

	//判断是否有违规操作
	if len(collectionIds) != 0 {
		result := database.Client.Where("id IN (?)", collectionIds).Where("user_id  = ?",
			userId).Find(&models.Collection{})
		if err := result.Error; err != nil {
			logger.Errorf("collection select by user_id from db failed %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}

		//恶意调用他人收藏夹
		if result.RowsAffected != int64(len(collectionIds)) {
			logger.Errorf("fobidden operation for user")
			response.FailWithMsg(c, http.StatusForbidden, strings.UserNotAuthorErrorCode, strings.UserNotAuthorError)
			return
		}

		//走一遍查询看看是否收藏过了
		result = database.Client.Where("user_id = ?", userId).
			Where("collection_id in (?) ", collectionIds).Where("video_id = ?", videoId).
			First(&models.UserVideoCollectionRelation{})
		if err := result.Error; err != nil {
			logger.Errorf("collection select by user_id from db failed %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}
		if result.RowsAffected != 0 {
			response.SuccessWithMsg(c, strings.UserVideoCollectionRelationExistsErrorCode, strings.UserVideoCollectionRelationExistsError)
			return
		}

		for i := 0; i < len(userVideoCollectionRelationList); i++ {
			userVideoCollectionRelationList[i].VideoId = videoId
			userVideoCollectionRelationList[i].UserId = userId
			userVideoCollectionRelationList[i].CollectionId = collectionIds[i]
		}

		//收藏
		if err = tx.Model(&models.UserVideoCollectionRelation{}).Create(&userVideoCollectionRelationList).Error; err != nil {
			logger.Errorf("save user_video_collection_relations to db failed  %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
			return
		}
	} else {
		result := tx.Where("user_id = ?", userId).Where("collection_id = 0").Where("video_id = ?", videoId).
			Find(&models.UserVideoCollectionRelation{})
		if err := result.Error; err != nil {
			logger.Errorf("collection select by user_id from db failed %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}
		if result.RowsAffected != 0 {
			response.SuccessWithMsg(c, strings.UserVideoCollectionRelationExistsErrorCode, strings.UserVideoCollectionRelationExistsError)
			return
		}
		if err := tx.Model(&models.UserVideoCollectionRelation{}).Create(&models.UserVideoCollectionRelation{
			UserId:  userId,
			VideoId: videoId,
		}).Error; err != nil {
			logger.Errorf("save user_video_collection_relations to db failed  %v", err)
			response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
			return
		}
	}

	response.Success(c, strings.SuccessCode)
	logger.Errorf("saveVideoToCollection success")
}

func (*ApiCollectionImpl) RemoveVideoFromCollectionById(c *gin.Context, id int) {

	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	removeVideoFromCollectionBatchReq := api.RemoveVideoFromCollectionBatchReq{}
	err := c.ShouldBindJSON(&removeVideoFromCollectionBatchReq)
	//400
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}

	//移除多个视频
	videoIds := removeVideoFromCollectionBatchReq.VideoIds
	collection := models.Collection{}

	logger := logging.LogService("Collections.Videos.RemoveVideoFromCollectionById").WithFields(logrus.Fields{
		"user_id":   userId,
		"id":        id,
		"video_ids": removeVideoFromCollectionBatchReq.VideoIds,
	})

	//404
	if err := database.Client.Where("id = ?", id).First(&collection).Error; err != nil {
		response.FailWithMsg(c, http.StatusNotFound, strings.NotFoundErrorCode,
			strings.NotFoundError)
		return
	}

	if collection.UserId != userId {
		logger.Errorf("fobidden operation for user")
		response.FailWithMsg(c, http.StatusForbidden, strings.UserNotAuthorErrorCode, strings.UserNotAuthorError)
		return
	}

	//开启事务
	tx := database.Client.WithContext(c).Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
			logger.Errorf("rollback:%v", err)
			return
		}
		tx.Commit()
	}()

	if err = tx.Where("user_id = ?", userId).Where("collection_id = ?", id).
		Where("video_id IN (?)", videoIds).Delete(&models.UserVideoCollectionRelation{}).Error; err != nil {
		logger.Errorf("delete user_video_collection_relations from db failed  %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseDeleteErrorCode, strings.InternalServiceError)
		return
	}

	response.Success(c, strings.SuccessCode)
	logger.Errorf("removeVideoFromCollectionById success")
}

func (*ApiCollectionImpl) RemoveVideoFromCollection(c *gin.Context) {

	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	removeVideoFromCollectionBatchReq := api.RemoveVideoFromCollectionBatchReq{}
	err := c.ShouldBindJSON(&removeVideoFromCollectionBatchReq)

	//400
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}
	videoIds := removeVideoFromCollectionBatchReq.VideoIds
	logger := logging.LogService("Collections.Videos.RemoveVideoFromCollection").WithFields(logrus.Fields{
		"user_id":   userId,
		"video_ids": videoIds,
	})
	//开启事务
	tx := database.Client.WithContext(c).Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
			logger.Errorf("rollback:%v", err)
			return
		}
		tx.Commit()
	}()

	if err = tx.Where("user_id = ?", userId).
		Where("video_id IN (?)", videoIds).Delete(&models.UserVideoCollectionRelation{}).Error; err != nil {
		logger.Errorf("remove user_video_collection_relations from db failed %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseDeleteErrorCode, strings.InternalServiceError)
		return
	}
	response.Success(c, strings.SuccessCode)
	logger.Errorf("removeVideoFromCollection success")
}
func (*ApiCollectionImpl) PageVideoFromCollectionById(c *gin.Context, id int, params api.PageVideoFromCollectionByIdParams) {
	pageNo := params.PageNo
	pageSize := params.PageSize
	offset := (pageNo - 1) * pageSize

	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Collections.Videos.PageVideoFromCollectionById").WithFields(logrus.Fields{
		"user_id":   userId,
		"page_size": pageSize,
		"page_no":   pageNo,
		"params":    params,
		"id":        id,
	})

	//404
	collection := models.Collection{}
	if err := database.Client.Where("id = ?", id).First(&collection).Error; err != nil {
		response.FailWithMsg(c, http.StatusNotFound, strings.NotFoundErrorCode,
			strings.NotFoundError)
		return
	}

	//403
	if userId != collection.UserId {
		logger.Errorf("fobidden operation for user")
		response.FailWithMsg(c, http.StatusForbidden, strings.UserNotAuthorErrorCode, strings.UserNotAuthorError)
		return
	}

	var videos []models.Video
	result := database.Client.Model(&models.UserVideoCollectionRelation{}).
		Select("videos.id , videos.file_name").
		Joins("join videos on videos.id = user_video_collection_relations.video_id").
		Where("user_video_collection_relations.collection_id = ?", id).
		Order("id").
		Limit(pageSize).
		Offset(offset).Group("videos.id").Find(&videos)

	if err := result.Error; err != nil {
		logger.Errorf("videos select from db failed  %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode,
			strings.InternalServiceError)
		return
	}
	//总数
	var count int64
	result.Count(&count)

	items := make([]api.VideoInfo, len(videos))
	if err := videoService.ToVideoInfo(videos, &userId, &items); err != nil {
		logger.Errorf("videos convert to info failed, %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode,
			strings.InternalServiceError)
		return
	}

	pageCount := count / int64(pageSize)

	if pageCount%int64(pageSize) != 0 {
		pageCount++
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, &api.VideoPagedListResp{
		PageNo:    &pageNo,
		PageSize:  &pageSize,
		Items:     &items,
		PageCount: &pageCount,
		Count:     &count,
	})
	logger.Errorf("success")
}
func (*ApiCollectionImpl) ListAllVideosInCollection(c *gin.Context, params api.ListAllVideosInCollectionParams) {
	pageNo := params.PageNo
	pageSize := params.PageSize
	offset := (pageNo - 1) * pageSize
	var videos []models.Video

	//401
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Collections.ListAllVideosInCollection").WithFields(logrus.Fields{
		"user_id":   userId,
		"page_size": pageSize,
		"page_no":   pageNo,
		"params":    params,
	})

	result := database.Client.Model(&models.UserVideoCollectionRelation{}).
		Select("videos.id , videos.file_name").
		Joins("join videos on videos.id = user_video_collection_relations.video_id").
		Where("user_video_collection_relations.user_id = ?", userId).
		Order("id").
		Limit(pageSize).
		Offset(offset).Group("videos.id").Find(&videos)

	if err := result.Error; err != nil {
		logger.Errorf("videos select from db failed  %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode,
			strings.InternalServiceError)
		return
	}
	//总数
	var count int64
	result.Count(&count)

	items := make([]api.VideoInfo, len(videos))
	if err := videoService.ToVideoInfo(videos, &userId, &items); err != nil {
		logger.Errorf("videos convert to info failed, %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode,
			strings.InternalServiceError)
		return
	}

	pageCount := count / int64(pageSize)

	if pageCount%int64(pageSize) != 0 {
		pageCount++
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, &api.VideoPagedListResp{
		PageNo:    &pageNo,
		PageSize:  &pageSize,
		Items:     &items,
		PageCount: &pageCount,
		Count:     &count,
	})

	logger.Errorf("success")
}
